package com.wanxi.education.controller.pay;


import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.wanxi.education.common.CommonResult;
import com.wanxi.education.config.ConfigUtil;
import com.wanxi.education.config.WXPayRequest;
import com.wanxi.education.service.OrderFrontService;
import com.wanxi.education.util.CommonUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 微信支付控制层
 *
 * @author: 南独酌酒 <211425401@126.com>
 * @date: 2020/10/9 17:07
 */
@RestController
@RequestMapping(value = "/pay")
public class WeiXinPayController {

    @Autowired
    private ConfigUtil configUtil;

    @Autowired
    private WXPayRequest wxPayRequest;

    private boolean autoReport;

    private WXPayConstants.SignType signType;

	private static final DecimalFormat df = new DecimalFormat("#");

	@Autowired
    private OrderFrontService orderFrontService;

	/**
     * 生成微信支付二维码
     *
     * @param outTradeNo 订单号
     * @param totalFee   金额(分)
     */
    @RequestMapping(value = "/createNative")
    public Map<String, String> createNative(@RequestParam("outTradeNo") String outTradeNo,
                                            @RequestParam("totalFee") String totalFee,
                                            HttpServletRequest request, HttpServletResponse response) {
        try {
            //todo 创建请求参数
            SortedMap<String, String> req = new TreeMap<String, String>();
            req.put("appid", ConfigUtil.APPID);    //公众号
            req.put("body", "测试产品001"); // 商品描述
            req.put("mch_id", ConfigUtil.MCH_ID);  // 商户号
            req.put("nonce_str", WXPayUtil.generateNonceStr()); // 32位随机字符串
            req.put("notify_url", "http://hskdemo.natapp1.cc/pay/notify");  // 回调地址
            req.put("out_trade_no", outTradeNo);   // 商户订单号
            req.put("spbill_create_ip", "222.209.32.93");   // 终端IP
            req.put("total_fee", df.format(Double.parseDouble(totalFee)));    // 标价金额(分)
            req.put("trade_type", "NATIVE");    // 交易类型

            //todo 生成要发送的 xml
            String xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);
            System.err.println(String.format("微信支付预下单请求 xml 格式:\n%s", xmlBody));

            //todo 发送 POST 请求 统一下单 API 并携带 xmlBody 内容,然后获得返回接口结果
            String result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xmlBody);

            System.err.println(String.format("%s", result));

            //todo 将返回结果从 xml 格式转换为 map 格式
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
            Map<String, String> map = new HashMap<>();
            map.put("code_url", resultMap.get("code_url")); // 支付地址
            map.put("total_fee", totalFee); // 总金额
            map.put("out_trade_no", outTradeNo);    // 订单号
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 查询订单支付状态
     *
     * @param outTradeNo 订单号
     * @return 支付状态
     */
    @GetMapping(value = "/queryOrder")
    public CommonResult queryOrder(@RequestParam("outTradeNo") String outTradeNo) {

            // 调用查询微信支付订单状态方法
            Map<String, String> map = this.queryPayStatus(outTradeNo);
            String transactionId = null;
            if (map == null) {
                return CommonResult.failure("支付失败", 0);
            }
            if (map.get("trade_state").equals("SUCCESS")) {
                //修改订单状态为已完成
                transactionId = map.get("transaction_id");
                orderFrontService.payMoney(outTradeNo, transactionId);
                return CommonResult.success("支付成功！", 1);
            }
        return CommonResult.failure("支付失败", 0);
    }

    /**
     * 查询微信支付订单状态
     *
     * @param outTradeNo 订单号
     * @return 支付状态
     */
    private Map<String, String> queryPayStatus(String outTradeNo) {
        try {
            //todo 创建请求参数
            SortedMap<String, String> req = new TreeMap<String, String>();
            req.put("appid", ConfigUtil.APPID); // 公众号ID
            req.put("mch_id", ConfigUtil.MCH_ID);   // 商户号
            req.put("out_trade_no", outTradeNo);    // 订单号
            req.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
            req.put("sign", WXPayUtil.generateSignature(req, ConfigUtil.API_KEY, WXPayConstants.SignType.MD5));

            //todo 生成要发送的 xml
            String xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);
            System.err.println(String.format("查询订单支付状态 xml 格式:\n%s", xmlBody));

            //todo 调用查询订单支付状态 API
            String result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/orderquery", "POST", xmlBody);

            //todo 返回解析后的 map 数据
            Map<String, String> map = null;
            if (result != null) {
                map = WXPayUtil.xmlToMap(result);
            }
            System.out.println(String.format("%s", map));
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 微信支付异步通知
     */
    @RequestMapping(value = "/notify")
    public String payNotify(HttpServletRequest request) {
        InputStream is = null;
        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
        try {
            is = request.getInputStream();
            // 将InputStream转换成String
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            try {
                // 转换成map
                Map<String, String> resultMap = WXPayUtil.xmlToMap(sb.toString());
                WXPay wxpayApp = new WXPay(new ConfigUtil());
                if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
                    String returnCode = resultMap.get("return_code");  //状态
                    String outTradeNo = resultMap.get("out_trade_no");//商户订单号
                    String transactionId = resultMap.get("transaction_id");
                    if (returnCode.equals("SUCCESS")) {
                        if (StringUtils.isNotBlank(outTradeNo)) {
                            /**
                             * 注意！！！
                             * 请根据业务流程，修改数据库订单支付状态，和其他数据的相应状态
                                    */
                            orderFrontService.payMoney(outTradeNo,transactionId);

                            System.out.println("微信手机支付回调成功,订单号:{}"+ outTradeNo);
                            System.out.println("微信手机支付回调成功,流水号:{}"+transactionId);
                            xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                            return xmlBack;
                        }
                    }
                    else {
                            orderFrontService.payFailed(outTradeNo);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            System.out.println("微信手机支付回调通知失败：" + e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return xmlBack;
    }

    /**
     * 微信支付退款
     */
    @RequestMapping(value = "/payRefund")
    public void payRefund(@RequestParam("outTradeNo") String outTradeNo,
                          @RequestParam("totalFee") String totalFee) throws Exception {
        SortedMap<String, String> req = new TreeMap<String, String>();
        req.put("notify_url", "http://hskdemo.natapp1.cc/pay/notify");  // 回调地址
        req.put("out_trade_no", outTradeNo);   // 商户订单号
//        req.put("transaction_id", outTradeNo);   // 微信支付订单号
        req.put("out_refund_no", outTradeNo);   // 商户退款单号
        req.put("total_fee", df.format(Double.parseDouble(totalFee)));    // 订单金额(分)
        req.put("refund_fee", df.format(Double.parseDouble(totalFee)));    // 退款金额(分)
        this.signType = WXPayConstants.SignType.MD5;
        Map<String, String> refund = this.refund(this.fillRequestData(req));
        System.out.println(JSONObject.toJSONString(refund));
    }


    /**
     * 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
     * 该函数适用于商户适用于统一下单等接口，不适用于红包、代金券接口
     *
     * @param reqData
     * @return
     * @throws Exception
     */
    public Map<String, String> fillRequestData(Map<String, String> reqData) throws Exception {
        reqData.put("appid", configUtil.getAppID());
        reqData.put("mch_id", configUtil.getMchID());
        reqData.put("nonce_str", WXPayUtil.generateNonceStr());

        //可加入可不加
        if (WXPayConstants.SignType.MD5.equals(this.signType)) {
            reqData.put("sign_type", WXPayConstants.MD5);
        }
        else if (WXPayConstants.SignType.HMACSHA256.equals(this.signType)) {
            reqData.put("sign_type", WXPayConstants.HMACSHA256);
        }


        reqData.put("sign", WXPayUtil.generateSignature(reqData, configUtil.getKey(), this.signType));
        return reqData;
    }


    /**
     * 作用：关闭订单<br>
     * 场景：公共号支付、扫码支付、APP支付
     * @param reqData 向wxpay post的请求数据
     * @return API返回数据
     * @throws Exception
     */
    public Map<String, String> closeOrder(Map<String, String> reqData) throws Exception {
        String url = "/pay/closeorder";

        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData));
        return this.processResponseXml(respXml);
    }

    /**
     * 作用：申请退款<br>
     * 场景：刷卡支付、公共号支付、扫码支付、APP支付<br>
     * 其他：需要证书
     * @param reqData 向wxpay post的请求数据
     * @return API返回数据
     * @throws Exception
     */
    public Map<String, String> refund(Map<String, String> reqData) throws Exception {
        String url = "/secapi/pay/refund";
        String respXml = this.requestWithCert(url, this.fillRequestData(reqData));
        return this.processResponseXml(respXml);
    }

    /**
     * 作用：退款查询<br>
     * 场景：刷卡支付、公共号支付、扫码支付、APP支付
     * @param reqData 向wxpay post的请求数据
     * @return API返回数据
     * @throws Exception
     */
    public Map<String, String> refundQuery(Map<String, String> reqData) throws Exception {
        String url = "/pay/refundquery";

        String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData));
        return this.processResponseXml(respXml);
    }

    /**
     * 需要证书的请求
     * @param urlSuffix String
     * @param reqData 向wxpay post的请求数据  Map
     * @return API返回数据
     * @throws Exception
     */
    public String requestWithCert(String urlSuffix, Map<String, String> reqData) throws Exception {
        String msgUUID= reqData.get("nonce_str");
        String reqBody = WXPayUtil.mapToXml(reqData);

        String resp = this.wxPayRequest.requestWithCert(urlSuffix, msgUUID, reqBody);
        return resp;
    }

    /**
     * 不需要证书的请求
     * @param urlSuffix String
     * @param reqData 向wxpay post的请求数据
     * @return API返回数据
     * @throws Exception
     */
    public String requestWithoutCert(String urlSuffix, Map<String, String> reqData) throws Exception {
        String msgUUID = reqData.get("nonce_str");
        String reqBody = WXPayUtil.mapToXml(reqData);

        String resp = this.wxPayRequest.requestWithoutCert(urlSuffix, msgUUID, reqBody);
        return resp;
    }

    /**
     * 处理 HTTPS API返回数据，转换成Map对象。return_code为SUCCESS时，验证签名。
     * @param xmlStr API返回的XML格式数据
     * @return Map类型数据
     * @throws Exception
     */
    public Map<String, String> processResponseXml(String xmlStr) throws Exception {
        String RETURN_CODE = "return_code";
        String return_code;
        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
        if (respData.containsKey(RETURN_CODE)) {
            return_code = respData.get(RETURN_CODE);
        }
        else {
            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
        }

        if (return_code.equals(WXPayConstants.FAIL)) {
            return respData;
        }
        else if (return_code.equals(WXPayConstants.SUCCESS)) {
            if (this.isResponseSignatureValid(respData)) {
                return respData;
            }
            else {
                throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
            }
        }
        else {
            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
        }
    }

    /**
     * 判断xml数据的sign是否有效，必须包含sign字段，否则返回false。
     *
     * @param reqData 向wxpay post的请求数据
     * @return 签名是否有效
     * @throws Exception
     */
    public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
        // 返回数据的签名方式和请求中给定的签名方式是一致的
        return WXPayUtil.isSignatureValid(reqData, configUtil.getKey(), this.signType);
    }
}
